package com.h3c.ptability.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eos.common.custom.BusinessException;
import com.google.common.base.Charsets;
import com.h3c.ptability.bo.ElectronicSignBO;
import com.h3c.ptability.cache.UserCache;
import com.h3c.ptability.cache.UserSignCache;
import com.h3c.ptability.dto.EditWaitSignTimeDTO;
import com.h3c.ptability.dto.ExcelFileDTO;
import com.h3c.ptability.dto.PtWaitSignInfoDTO;
import com.h3c.ptability.entity.PtAllWorker;
import com.h3c.ptability.entity.PtEsignRecord;
import com.h3c.ptability.entity.PtEsignScene;
import com.h3c.ptability.entity.PtWaitSignInfo;
import com.h3c.ptability.enums.ElectronicSignRecordStatus;
import com.h3c.ptability.mapper.PtWaitSignInfoMapper;
import com.h3c.ptability.mapstruct.PtWaitSignInfoMapstruct;
import com.h3c.ptability.service.*;
import com.h3c.ptability.support.ReturnCodeEnum;
import com.h3c.ptability.support.SignExcelInfo;
import com.h3c.ptability.support.event.EventElement;
import com.h3c.ptability.utils.AppBusinessException;
import com.h3c.ptability.utils.PageUtils;
import com.h3c.ptability.vo.PtWaitSignInfoDownloadVO;
import com.h3c.ptability.vo.PtWaitSignInfoVO;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 实现类
 *
 * @author chenyifan
 * @since 2022-10-20 15:08:04
 */
@Service
@AllArgsConstructor
public class PtWaitSignInfoServiceImpl extends ServiceImpl<PtWaitSignInfoMapper, PtWaitSignInfo> implements IPtWaitSignInfoService {

    private final CommService commService;

    private final IElectronicSignService electronicSignService;

    private final IPtEsignRecordService ptEsignRecordService;

    private final IPtEnumService ptEnumService;

    private final IPtEsignSceneService sceneService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void parseFileAndSaveWaitSign(MultipartFile file, String sceneId, String appId) {
        PtEsignScene scene = null;
        String fileContent = "";
        if (StrUtil.isNotBlank(sceneId)) {
            scene = sceneService.getById(sceneId);
        }
        if (Objects.nonNull(scene)) {
            if (scene.getSceneType().equals(PtEsignRecord.TRAINING_SERVICE_AGREEMENT_TYPE)) {
                fileContent = commService.parseExcelByTemplate("PT_TRAINING_SERVICE_WAIT_SIGN_IMPORT", file);
            } else if (scene.getSceneType().equals(PtEsignRecord.INTERNSHIP_BEFORE_AGREEMENT_TYPE)) {
                fileContent = commService.parseExcelByTemplate("PT_INTERNSHIP_BEFORE_AGREEMENT_WAIT_SIGN_IMPORT", file);
            } else if (scene.getSceneType().equals(PtEsignRecord.INTERNSHIP_AFTER_AGREEMENT_TYPE)) {
                fileContent = commService.parseExcelByTemplate("PT_INTERNSHIP_AFTER_AGREEMENT_WAIT_SIGN_IMPORT", file);
            } else if (scene.getSceneType().equals(PtEsignRecord.CAMPUS_HEADHUNTING_TYPE)) {
                fileContent = commService.parseExcelByTemplate("PT_CAMPUS_HEADHUNTING_WAIT_SIGN_IMPORT", file);
            }
        } else {
            fileContent = commService.parseExcelByTemplate("PT_WAIT_SIGN_IMPORT", file);
        }
        if (StrUtil.isBlank(fileContent)) {
            throw new AppBusinessException("解析excel模板出错");
        }
        List<PtWaitSignInfoDTO> waitSignInfoDTOs = JSONUtil.toList(JSONUtil.parseArray(fileContent), PtWaitSignInfoDTO.class);
        ArrayList<PtWaitSignInfo> waitSignInfos = new ArrayList<>(waitSignInfoDTOs.size());
        for (PtWaitSignInfoDTO waitSignInfoDTO : waitSignInfoDTOs) {
            PtWaitSignInfo ptWaitSignInfo = PtWaitSignInfoMapstruct.INSTANCE.dtoToEntity(waitSignInfoDTO);
            ptWaitSignInfo.setAppId(appId);
            ptWaitSignInfo.setSignExcelInfo(PtWaitSignInfoMapstruct.INSTANCE.dtoToSignExcelInfo(waitSignInfoDTO));
            waitSignInfos.add(ptWaitSignInfo);
        }
        ArrayList<PtWaitSignInfo> needUpdateList = new ArrayList<>();
        ArrayList<PtWaitSignInfo> needInsertList = new ArrayList<>();
        Date now = new Date();
        for (PtWaitSignInfo s : waitSignInfos) {
            if (Objects.nonNull(scene)) {
                s.setSceneType(String.valueOf(scene.getSceneType()));
            }
            s.setSceneId(sceneId);
            if (Objects.nonNull(scene.getFormal()) && scene.getFormal() == 0) {
                if (StrUtil.isBlank(s.getSignExcelInfo().getCertNo()) || !IdcardUtil.isValidCard(s.getSignExcelInfo().getCertNo())) {
                    throw new AppBusinessException(s.getWorkerName().concat("的身份证号错误"));
                }
                if (StrUtil.isBlank(s.getSignExcelInfo().getPhoneNo()) || !PhoneUtil.isPhone(s.getSignExcelInfo().getPhoneNo())) {
                    throw new AppBusinessException(s.getWorkerName().concat("的手机号错误"));
                }
            }
            if (StringUtils.hasText(s.getWorkerId())) {
                PtAllWorker userByUserId = UserCache.getLoginUserByUserId(s.getWorkerId());
                s.setWorkerName(userByUserId.getWorkerName());
                s.getSignExcelInfo().setWorkerName(userByUserId.getWorkerName());
            }
            s.setImportTime(now);
            s.setImportOperator(UserCache.getLoginUserNameAndId());
            PtWaitSignInfo waitSignInfo = getOne(new QueryWrapper<PtWaitSignInfo>().lambda()
                    .eq(PtWaitSignInfo::getSceneType, s.getSceneType())
                    .eq(StrUtil.isNotBlank(s.getWorkerId()), PtWaitSignInfo::getWorkerId, s.getWorkerId())
                    .apply(Objects.nonNull(s.getSignExcelInfo()) && StrUtil.isNotBlank(s.getSignExcelInfo().getCertNo()),
                            "sign_excel_info -> '$.certNo' = {0}", s.getSignExcelInfo().getCertNo())
            );
            if (Objects.isNull(waitSignInfo)) {
                needInsertList.add(s);
            } else {
                s.setId(waitSignInfo.getId());
                needUpdateList.add(s);
            }
        }
        updateBatchById(needUpdateList);
        saveBatch(needInsertList);
    }

    @Override
    public Boolean judgeIfWaitSign(String workerId, String sceneType) {
        Date now = new Date();
        PtWaitSignInfo waitSignInfo = getOne(new QueryWrapper<PtWaitSignInfo>().lambda()
                .eq(PtWaitSignInfo::getWorkerId, workerId)
                .eq(PtWaitSignInfo::getSceneType, sceneType)
        );
        if(StringUtils.hasText(waitSignInfo.getRecordId())){
            PtEsignRecord esignRecord = ptEsignRecordService.getById(waitSignInfo.getRecordId());
            if(ElectronicSignRecordStatus.WASTE_SIGN_FINISH.equals(esignRecord.getSignStatus())){
                throw new AppBusinessException("已作废，无需重新发起");
            }
        }
        if (Objects.isNull(waitSignInfo)) {
            throw new AppBusinessException("当前用户不存在待签署名单中");
        }
        if (now.before(waitSignInfo.getPlanStartTime())) {
            throw new AppBusinessException("未达到用户计划签署开始时间！");
        }
        if (now.after(waitSignInfo.getPlanEndTime())) {
            throw new AppBusinessException("超过用户计划签署结束时间！");
        }
        return true;
    }

    @Override
    public void signWaitForMan(ElectronicSignBO electronicSignBO, Boolean isRetry) throws Exception {
        List<JSONObject> userInfoList = JSONUtil.toList(electronicSignBO.getUserData().getJSONArray(ElectronicSignBO.USER_INFO_LIST), JSONObject.class);
        if (userInfoList.size() == 0) {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "无用户信息");
        }
        JSONObject userInfo = userInfoList.get(0);
        String workerId = userInfo.getStr(ElectronicSignBO.WORKER_ID);
        PtWaitSignInfo waitSignInfo = getOne(new QueryWrapper<PtWaitSignInfo>().lambda()
                .eq(PtWaitSignInfo::getWorkerId, workerId)
                .eq(PtWaitSignInfo::getSceneType, electronicSignBO.getSceneType())
        );
        if (Objects.isNull(waitSignInfo)) {
            throw new AppBusinessException("当前用户不存在待签署名单中");
        }
        if (StringUtils.hasText(waitSignInfo.getRecordId()) || !UserSignCache.setUserSignFlag(workerId)) {
            if (!Arrays.asList(1, 3).contains(ptEsignRecordService.getById(waitSignInfo.getRecordId()).getSignStatus().getCode())) {
                throw new AppBusinessException("请勿重复提交签署");
            }
        }
        String failWorkerIds = electronicSignService.finalSignFile(electronicSignBO, isRetry);
        if (StringUtils.hasText(failWorkerIds)) {
            throw new AppBusinessException("员工工号为:" + failWorkerIds + "的电子签章提交失败，原因:场景存在异常记录");
        }

    }

    @Override
    public void fillRecordId(EventElement eventElement) {
        if (eventElement.getIsCancel()) {
            update(null, new UpdateWrapper<PtWaitSignInfo>().lambda()
                    .set(PtWaitSignInfo::getRecordId, null)
                    .eq(PtWaitSignInfo::getRecordId, eventElement.getRecordId())
            );
        } else {
            update(null, new UpdateWrapper<PtWaitSignInfo>().lambda()
                    .set(PtWaitSignInfo::getRecordId, eventElement.getRecordId())
                    .eq(PtWaitSignInfo::getSceneType, eventElement.getSceneType())
                    .eq(StringUtils.hasText(eventElement.getWorkerId()), PtWaitSignInfo::getWorkerId, eventElement.getWorkerId())
                    .apply(StringUtils.hasText(eventElement.getCertNo()), "sign_excel_info -> '$.certNo' = {0}", eventElement.getCertNo())
            );
        }
    }

    @Override
    public void editWaitSignTime(EditWaitSignTimeDTO editWaitSignTimeDTO) {
        update(null, new UpdateWrapper<PtWaitSignInfo>().lambda()
                .set(PtWaitSignInfo::getPlanStartTime, editWaitSignTimeDTO.getPlanStartTime())
                .set(PtWaitSignInfo::getPlanEndTime, editWaitSignTimeDTO.getPlanEndTime())
                .eq(PtWaitSignInfo::getId, editWaitSignTimeDTO.getId())
        );
    }

    @Override
    public IPage<PtWaitSignInfoVO> pageWaitSignInfo(PtWaitSignInfoDTO waitSignInfoDTO, Integer currentPage, Integer pageSize) {
        LambdaQueryWrapper<PtWaitSignInfo> queryWrapper = new QueryWrapper<PtWaitSignInfo>().lambda();
        if (Objects.nonNull(waitSignInfoDTO)) {
            if (StringUtils.hasText(waitSignInfoDTO.getWorkerId())) {
                List<String> workerIdList = StrUtil.splitTrim(waitSignInfoDTO.getWorkerId(), ",");
                queryWrapper.in(PtWaitSignInfo::getWorkerId, workerIdList);
            }
            if (StringUtils.hasText(waitSignInfoDTO.getSceneType())) {
                queryWrapper.eq(PtWaitSignInfo::getSceneType, waitSignInfoDTO.getSceneType());
            }
            if (StringUtils.hasText(waitSignInfoDTO.getAppId())) {
                queryWrapper.eq(PtWaitSignInfo::getAppId, waitSignInfoDTO.getAppId());
            }
        }
        Page<PtWaitSignInfo> signInfoPage = page(new Page<>(currentPage, pageSize), queryWrapper);
        List<PtWaitSignInfo> ptWaitSignInfos = signInfoPage.getRecords();
        ptWaitSignInfos.forEach(s -> {
            if (StringUtils.hasText(s.getRecordId())) {
                PtEsignRecord esignRecord = ptEsignRecordService.getById(s.getRecordId());
                s.setSignStatus(esignRecord.getSignStatus().getStatusName());
                s.setSignStatusIntType(esignRecord.getSignStatus().getCode());
            } else {
                s.setSignStatus("待发起");
            }
        });
        return PageUtils.toPage(signInfoPage, PtWaitSignInfoMapstruct.INSTANCE.entityListToVOList(ptWaitSignInfos));
    }

    @Override
    @SneakyThrows
    public void downloadRecord(PtWaitSignInfoDTO signInfoDTO, HttpServletResponse response) {
        List<PtWaitSignInfo> waitSignInfoList = null;
        if (StringUtils.hasText(signInfoDTO.getSceneType()) && Arrays.asList(
                PtEsignRecord.INTERNSHIP_BEFORE_AGREEMENT_TYPE,
                PtEsignRecord.INTERNSHIP_AFTER_AGREEMENT_TYPE,
                PtEsignRecord.CAMPUS_HEADHUNTING_TYPE
        ).contains(Integer.parseInt(signInfoDTO.getSceneType()))) {
            waitSignInfoList = baseMapper.downloadExcelRecord(signInfoDTO);
        } else {
            waitSignInfoList = baseMapper.downloadRecord(signInfoDTO);
        }
        List<PtWaitSignInfoDownloadVO> downloadVOS = PtWaitSignInfoMapstruct.INSTANCE.entityListToDownloadVOList(waitSignInfoList);
        ExcelFileDTO excelFileDTO = new ExcelFileDTO();
        excelFileDTO.setAppId("ptability-manage");
        excelFileDTO.setDataList(downloadVOS);
        excelFileDTO.setFileName("待签署名单签署记录".concat(DateUtil.format(new Date(), "yyyy年MM月dd日")));
        excelFileDTO.setTemplateCode("PT_WAIT_ESIGN_RECORD_EXPORT");
        File downLoadFile = ptEnumService.getExcelFile(excelFileDTO);
        FileInputStream inputStream = new FileInputStream(downLoadFile);
        byte[] buffer = new byte[(int) downLoadFile.length()];
        inputStream.read(buffer);
        inputStream.close();
        FileUtil.del(downLoadFile);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(Charsets.UTF_8.name());
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(downLoadFile.getName(), Charsets.UTF_8.name()));
        response.setHeader("issuccess", "1");
        response.setHeader("Access-Control-Expose-Headers", "Content-disposition,issuccess");
        IoUtil.write(response.getOutputStream(), true, buffer);
    }

    @Override
    public void signExcelInfoWaitForMan(List<String> idList, String appId) throws Exception {
        if (idList.size() == 0) {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "请选择要发起签署的对象");
        }
        Date now = new Date();
        List<PtWaitSignInfo> waitSignInfoList = list(new QueryWrapper<PtWaitSignInfo>().lambda()
                .in(PtWaitSignInfo::getId, idList)
                .isNull(PtWaitSignInfo::getRecordId)
                .le(PtWaitSignInfo::getPlanStartTime, now)
                .ge(PtWaitSignInfo::getPlanEndTime, now)
        );
        if (waitSignInfoList.size() == 0) {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "无可发起对象");
        }
        List<SignExcelInfo> signExcelInfos = waitSignInfoList.stream().map(PtWaitSignInfo::getSignExcelInfo).collect(Collectors.toList());
        HashMap<String, List<SignExcelInfo>> userInfoList = new HashMap<>(1);
        userInfoList.put(ElectronicSignBO.USER_INFO_LIST, signExcelInfos);
        ElectronicSignBO electronicSignBO = new ElectronicSignBO();
        electronicSignBO.setSceneType(Integer.parseInt(waitSignInfoList.get(0).getSceneType()));
        electronicSignBO.setAppId(appId);
        electronicSignBO.setUserData(JSONUtil.parseObj(JSONUtil.toJsonStr(userInfoList)));
        String failWorkerIds = electronicSignService.finalSignFile(electronicSignBO, false);
        if (StringUtils.hasText(failWorkerIds)) {
            throw new AppBusinessException("员工工号为:" + failWorkerIds + "的电子签章提交失败，原因:场景存在异常记录");
        }
    }
}

